home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / pcl / pcl-rev4.lha / construct.lisp < prev    next >
Lisp/Scheme  |  1990-11-19  |  42KB  |  1,110 lines

  1. ;;;-*-Mode:LISP; Package:(PCL LISP 1000); Base:10; Syntax:Common-lisp -*-
  2. ;;;
  3. ;;; *************************************************************************
  4. ;;; Copyright (c) 1985, 1986, 1987, 1988, 1989, 1990 Xerox Corporation.
  5. ;;; All rights reserved.
  6. ;;;
  7. ;;; Use and copying of this software and preparation of derivative works
  8. ;;; based upon this software are permitted.  Any distribution of this
  9. ;;; software or derivative works must comply with all applicable United
  10. ;;; States export control laws.
  11. ;;; 
  12. ;;; This software is made available AS IS, and Xerox Corporation makes no
  13. ;;; warranty about the software, its performance or its conformity to any
  14. ;;; specification.
  15. ;;; 
  16. ;;; Any person obtaining a copy of this software is requested to send their
  17. ;;; name and post office or electronic mail address to:
  18. ;;;   CommonLoops Coordinator
  19. ;;;   Xerox PARC
  20. ;;;   3333 Coyote Hill Rd.
  21. ;;;   Palo Alto, CA 94304
  22. ;;; (or send Arpanet mail to CommonLoops-Coordinator.pa@Xerox.arpa)
  23. ;;;
  24. ;;; Suggestions, comments and requests for improvements are also welcome.
  25. ;;; *************************************************************************
  26. ;;;
  27. ;;;
  28. ;;; This file defines the defconstructor and other make-instance optimization
  29. ;;; mechanisms.
  30. ;;; 
  31.  
  32. (in-package 'pcl)
  33.  
  34. ;;;
  35. ;;; defconstructor is used to define special purpose functions which just
  36. ;;; call make-instance with a symbol as the first argument.  The semantics
  37. ;;; of defconstructor is that it is equivalent to defining a function which
  38. ;;; just calls make-instance. The purpose of defconstructor is to provide
  39. ;;; PCL with a way of noticing these calls to make-instance so that it can
  40. ;;; optimize them.  Specific ports of PCL could just have their compiler
  41. ;;; spot these calls to make-instance and then call this code.  Having the
  42. ;;; special defconstructor facility is the best we can do portably.
  43. ;;; 
  44. ;;;
  45. ;;; A call to defconstructor like:
  46. ;;;
  47. ;;;  (defconstructor make-foo foo (a b &rest r) a a :mumble b baz r)
  48. ;;;
  49. ;;; Is equivalent to a defun like:
  50. ;;;
  51. ;;;  (defun make-foo (a b &rest r)
  52. ;;;    (make-instance 'foo 'a a ':mumble b 'baz r))
  53. ;;;
  54. ;;; Calls like the following are also legal:
  55. ;;;
  56. ;;;  (defconstructor make-foo foo ())
  57. ;;;  (defconstructor make-bar bar () :x *x* :y *y*)
  58. ;;;  (defconstructor make-baz baz (a b c) a-b (list a b) b-c (list b c))
  59. ;;;
  60. ;;;
  61. ;;; The general idea of this implementation is that the expansion of the
  62. ;;; defconstructor form includes the creation of closure generators which
  63. ;;; can be called to create constructor code for the class.  The ways that
  64. ;;; a constructor can be optimized depends not only on the defconstructor
  65. ;;; form, but also on the state of the class and the generic functions in
  66. ;;; the initialization protocol.  Because of this, the determination of the
  67. ;;; form of constructor code to be used is a two part process.
  68. ;;;
  69. ;;; At compile time, make-constructor-code-generators looks at the actual
  70. ;;; defconstructor form and makes a list of appropriate constructor code
  71. ;;; generators.  All that is really taken into account here is whether
  72. ;;; any initargs are supplied in the call to make-instance, and whether
  73. ;;; any of those are constant.
  74. ;;;
  75. ;;; At constructor code generation time (see note about lazy evaluation)
  76. ;;; compute-constructor-code calls each of the constructor code generators
  77. ;;; to try to get code for this constructor.  Each generator looks at the
  78. ;;; state of the class and initialization protocol generic functions and
  79. ;;; decides whether its type of code is appropriate.  This depends on things
  80. ;;; like whether there are any applicable methods on initialize-instance,
  81. ;;; whether class slots are affected by initialization etc.
  82. ;;; 
  83. ;;;
  84. ;;; Constructor objects are funcallable instances, the protocol followed to
  85. ;;; to compute the constructor code for them is quite similar to the protocol
  86. ;;; followed to compute the discriminator code for a generic function.  When
  87. ;;; the constructor is first loaded, we install as its code a function which
  88. ;;; will compute the actual constructor code the first time it is called.
  89. ;;; 
  90. ;;; If there is an update to the class structure which might invalidate the
  91. ;;; optimized constructor, the special lazy constructor installer is put back
  92. ;;; so that it can compute the appropriate constructor when it is called.
  93. ;;; This is the same kind of lazy evaluation update strategy used elswhere
  94. ;;; in PCL.
  95. ;;;
  96. ;;; To allow for flexibility in the PCL implementation and to allow PCL users
  97. ;;; to specialize this constructor facility for their own metaclasses, there
  98. ;;; is an internal protocol followed by the code which loads and installs
  99. ;;; the constructors.  This is documented in the comments in the code.
  100. ;;;
  101. ;;; This code is also designed so that one of its levels, can be used to
  102. ;;; implement optimization of calls to make-instance which can't go through
  103. ;;; the defconstructor facility.  This has not been implemented yet, but the
  104. ;;; hooks are there.
  105. ;;;
  106. ;;;
  107.  
  108. (defmacro defconstructor
  109.       (name class lambda-list &rest initialization-arguments)
  110.   (expand-defconstructor class
  111.              name
  112.              lambda-list
  113.              (copy-list initialization-arguments)))
  114.  
  115. (defun expand-defconstructor (class-name name lambda-list supplied-initargs)
  116.   (let ((class (find-class class-name nil))
  117.     (supplied-initarg-names
  118.       (gathering1 (collecting)
  119.         (iterate ((name (*list-elements supplied-initargs :by #'cddr)))
  120.           (gather1 name)))))
  121.     (when (null class)
  122.       (error "defconstructor form being compiled (or evaluated) before~@
  123.               class ~S is defined."
  124.          class-name))
  125.     `(progn
  126.        ;; In order to avoid undefined function warnings, we want to tell
  127.        ;; the compile time environment that a function with this name and
  128.        ;; this argument list has been defined.  The portable way to do this
  129.        ;; is with defun.
  130.        (proclaim '(notinline ,name))
  131.        (defun ,name ,lambda-list
  132.      (declare (ignore ,@(specialized-lambda-list-parameters lambda-list)))
  133.      (error "Constructor ~S not loaded." ',name))
  134.  
  135.        ,(make-top-level-form `(defconstructor ,name)
  136.                  '(load eval)
  137.       `(load-constructor
  138.          ',class-name
  139.          ',(class-name (class-of class))
  140.          ',name
  141.          ',supplied-initarg-names
  142.          ;; make-constructor-code-generators is called to return a list
  143.          ;; of constructor code generators.  The actual interpretation
  144.          ;; of this list is left to compute-constructor-code, but the
  145.          ;; general idea is that it should be an plist where the keys
  146.          ;; name a kind of constructor code and the values are generator
  147.          ;; functions which return the actual constructor code.  The
  148.          ;; constructor code is usually a closures over the arguments
  149.          ;; to the generator.
  150.          ,(make-constructor-code-generators class
  151.                         name
  152.                         lambda-list
  153.                         supplied-initarg-names
  154.                         supplied-initargs))))))
  155.  
  156. (defun load-constructor (class-name metaclass-name constructor-name
  157.              supplied-initarg-names code-generators)
  158.   (let ((class (find-class class-name nil)))
  159.     (cond ((null class)
  160.        (error "defconstructor form being loaded (or evaluated) before~@
  161.                    class ~S is defined."
  162.           class-name))
  163.       ((neq (class-name (class-of class)) metaclass-name)
  164.        (error "When defconstructor ~S was compiled, the metaclass of the~@
  165.                    class ~S was ~S.  The metaclass is now ~S.~@
  166.                    The constructor must be recompiled."
  167.           constructor-name
  168.           class-name
  169.           metaclass-name
  170.           (class-name (class-of class))))
  171.       (t
  172.        (load-constructor-internal class
  173.                       constructor-name
  174.                       supplied-initarg-names
  175.                       code-generators)
  176.        constructor-name))))
  177.  
  178. ;;;
  179. ;;; The actual constructor objects.
  180. ;;; 
  181. (defclass constructor ()               
  182.      ((class                    ;The class with which this
  183.     :initarg :class                ;constructor is associated.
  184.     :reader constructor-class)        ;The actual class object,
  185.                         ;not the class name.
  186.                         ;      
  187.       (name                    ;The name of this constructor.
  188.     :initform nil                ;This is the symbol in whose
  189.     :initarg :name                ;function cell the constructor
  190.     :reader constructor-name)        ;usually sits.  Of course, this
  191.                         ;is optional.  defconstructor
  192.                         ;makes named constructors, but
  193.                         ;it is possible to manipulate
  194.                         ;anonymous constructors also.
  195.                         ;
  196.       (code-type                ;The type of code currently in
  197.     :initform nil                ;use by this constructor.  This
  198.     :accessor constructor-code-type)    ;is mostly for debugging and
  199.                         ;analysis purposes.
  200.                         ;The lazy installer sets this
  201.                         ;to LAZY.  The most basic and
  202.                         ;least optimized type of code
  203.                         ;is called FALLBACK.
  204.                         ;
  205.       (supplied-initarg-names            ;The names of the initargs this
  206.     :initarg :supplied-initarg-names    ;constructor supplies when it
  207.     :reader                    ;"calls" make-instance.
  208.        constructor-supplied-initarg-names)    ;
  209.                         ;
  210.       (code-generators                ;Generators for the different
  211.     :initarg :code-generators        ;types of code this constructor
  212.     :reader constructor-code-generators))    ;could use.
  213.   (:metaclass funcallable-standard-class))
  214.  
  215.  
  216. ;;;
  217. ;;; Because the value in the code-type slot should always correspond to the
  218. ;;; funcallable-instance-function of the constructor, this function should
  219. ;;; always be used to set the both at the same time.
  220. ;;;
  221. (defun set-constructor-code (constructor code type)
  222.   (set-funcallable-instance-function constructor code)
  223.   (set-function-name constructor (constructor-name constructor))
  224.   (setf (constructor-code-type constructor) type))
  225.  
  226.  
  227. (defmethod print-object ((constructor constructor) stream)
  228.   (printing-random-thing (constructor stream)
  229.     (format stream
  230.         "~S ~S (~S)"
  231.         (or (class-name (class-of constructor)) "Constructor")
  232.         (or (constructor-name constructor) "Anonymous")
  233.         (constructor-code-type constructor))))
  234.  
  235. (defmethod describe-object ((constructor constructor) stream)
  236.   (format stream
  237.       "~S is a constructor for the class ~S.~%~
  238.             The current code type is ~S.~%~
  239.             Other possible code types are ~S."
  240.       constructor (constructor-class constructor)
  241.       (constructor-code-type constructor)
  242.       (gathering1 (collecting)
  243.         (doplist (key val) (constructor-code-generators constructor)
  244.           (gather1 key)))))
  245.  
  246. ;;;
  247. ;;; I am not in a hairy enough mood to make this implementation be metacircular
  248. ;;; enough that it can support a defconstructor for constructor objects.
  249. ;;; 
  250. (defun make-constructor (class name supplied-initarg-names code-generators)
  251.   (make-instance 'constructor
  252.          :class class
  253.          :name name
  254.          :supplied-initarg-names supplied-initarg-names
  255.          :code-generators code-generators))
  256.  
  257. ; This definition actually appears in std-class.lisp.
  258. ;(defmethod class-constructors ((class std-class))
  259. ;  (with-slots (plist) class (getf plist 'constructors)))
  260.  
  261. (defmethod add-constructor ((class std-class)
  262.                 (constructor constructor))
  263.   (with-slots (plist) class
  264.     (pushnew constructor (getf plist 'constructors))))
  265.  
  266. (defmethod remove-constructor ((class std-class)
  267.                    (constructor constructor))
  268.   (with-slots (plist) class
  269.     (setf (getf plist 'constructors)
  270.       (delete constructor (getf plist 'constructors)))))
  271.  
  272. (defmethod get-constructor ((class std-class) name &optional (error-p t))
  273.   (or (dolist (c (class-constructors class))
  274.     (when (eq (constructor-name c) name) (return c)))
  275.       (if error-p
  276.       (error "Couldn't find a constructor with name ~S for class ~S."
  277.          name class)
  278.       ())))
  279.  
  280. ;;;
  281. ;;; This is called to actually load a defconstructor constructor.  It must
  282. ;;; install the lazy installer in the function cell of the constructor name,
  283. ;;; and also add this constructor to the list of constructors the class has.
  284. ;;; 
  285. (defmethod load-constructor-internal
  286.        ((class std-class) name initargs generators)
  287.   (let ((constructor (make-constructor class name initargs generators))
  288.     (old (get-constructor class name nil)))
  289.     (when old (remove-constructor class old))
  290.     (install-lazy-constructor-installer constructor)
  291.     (add-constructor class constructor)
  292.     (setf (symbol-function name) constructor)))
  293.  
  294. (defmethod install-lazy-constructor-installer ((constructor constructor))
  295.   (let ((class (constructor-class constructor)))
  296.     (set-constructor-code constructor
  297.               #'(lambda (&rest args)
  298.                   (multiple-value-bind (code type)
  299.                   (compute-constructor-code class constructor)
  300.                 (prog1 (apply code args)
  301.                        (set-constructor-code constructor
  302.                                  code
  303.                                  type))))
  304.               'lazy)))
  305.  
  306. ;;;
  307. ;;; The interface to keeping the constructors updated.
  308. ;;;
  309. ;;; add-method and remove-method (for standard-generic-function and -method),
  310. ;;; promise to call maybe-update-constructors on the generic function and
  311. ;;; the method.
  312. ;;; 
  313. ;;; The class update code promises to call update-constructors whenever the
  314. ;;; class is changed.  That is, whenever the supers, slots or options change.
  315. ;;; If user defined classes of constructor needs to be updated in more than
  316. ;;; these circumstances, they should use the dependent updating mechanism to
  317. ;;; make sure update-constructors is called.
  318. ;;;
  319. ;;; Bootstrapping concerns force the definitions of maybe-update-constructors
  320. ;;; and update-constructors to be in the file std-class.  For clarity, they
  321. ;;; also appear below.  Be sure to keep the definition here and there in sync.
  322. ;;; 
  323. ;(defvar *initialization-generic-functions*
  324. ;     (list #'make-instance
  325. ;           #'default-initargs
  326. ;           #'allocate-instance
  327. ;           #'initialize-instance
  328. ;           #'shared-initialize))
  329. ;
  330. ;(defmethod maybe-update-constructors
  331. ;       ((generic-function generic-function)
  332. ;        (method method))
  333. ;  (when (memq generic-function *initialization-generic-functions*)
  334. ;    (labels ((recurse (class)
  335. ;           (update-constructors class)
  336. ;           (dolist (subclass (class-direct-subclasses class))
  337. ;         (recurse subclass))))
  338. ;      (when (classp (car (method-specializers method)))
  339. ;    (recurse (car (method-specializers method)))))))
  340. ;
  341. ;(defmethod update-constructors ((class std-class))
  342. ;  (dolist (cons (class-constructors class))
  343. ;    (install-lazy-constructor-installer cons)))
  344. ;
  345. ;(defmethod update-constructors ((class class))
  346. ;  ())
  347.  
  348.  
  349.  
  350. ;;;
  351. ;;; Here is the actual smarts for making the code generators and then trying
  352. ;;; each generator to get constructor code. This extensible mechanism allows
  353. ;;; new kinds of constructor code types to be added. A programmer defining a
  354. ;;; specialization of the constructor class can either use this mechanism to
  355. ;;; define new code types, or can override this mechanism by overriding the
  356. ;;; methods on make-constructor-code-generators and compute-constructor-code.
  357. ;;;
  358. ;;; The function defined by define-constructor-code-type will receive the
  359. ;;; class object, and the 4 original arguments to defconstructor. It can
  360. ;;; return a constructor code generator, or return nil if this type of code
  361. ;;; is determined to not be appropriate after looking at the defconstructor
  362. ;;; arguments.
  363. ;;;
  364. ;;; When compute-constructor-code is called, it first performs basic checks
  365. ;;; to make sure that the basic assumptions common to all the code types are
  366. ;;; valid.  (For details see method definition).  If any of the tests fail,
  367. ;;; the fallback constructor code type is used.  If none of the tests fail,
  368. ;;; the constructor code generators are called in order.  They receive 5
  369. ;;; arguments:
  370. ;;;
  371. ;;;   CLASS        the class the constructor is making instances of
  372. ;;;   WRAPPER      that class's wrapper
  373. ;;;   DEFAULTS     the result of calling class-default-initargs on class
  374. ;;;   INITIALIZE   the applicable methods on initialize-instance
  375. ;;;   SHARED       the applicable methosd on shared-initialize
  376. ;;;
  377. ;;; The first code generator to return code is used.  The code generators are
  378. ;;; called in reverse order of definition, so define-constructor-code-type
  379. ;;; forms which define better code should appear after ones that define less
  380. ;;; good code.  The fallback code type appears first.  Note that redefining a
  381. ;;; code type does not change its position in the list.  To do that,  define
  382. ;;; a new type at the end with the behavior.
  383. ;;; 
  384.  
  385. (defvar *constructor-code-types* ())
  386.  
  387. (defmacro define-constructor-code-type (type arglist &body body)
  388.   (let ((fn-name (intern (format nil
  389.                  "CONSTRUCTOR-CODE-GENERATOR ~A ~A"
  390.                  (package-name (symbol-package type))
  391.                  (symbol-name type))
  392.              *the-pcl-package*)))
  393.     `(progn
  394.        (defun ,fn-name ,arglist .,body)
  395.        (load-define-constructor-code-type ',type ',fn-name))))
  396.  
  397. (defun load-define-constructor-code-type (type generator)
  398.   (let ((old-entry (assq type *constructor-code-types*)))
  399.     (if old-entry 
  400.     (setf (cadr old-entry) generator)
  401.     (push (list type generator) *constructor-code-types*))
  402.     type))
  403.  
  404. (defmethod make-constructor-code-generators
  405.        ((class std-class)
  406.         name lambda-list supplied-initarg-names supplied-initargs)
  407.   (cons 'list
  408.     (gathering1 (collecting)
  409.       (dolist (entry *constructor-code-types*)
  410.         (let ((generator
  411.             (funcall (cadr entry) class name lambda-list 
  412.                       supplied-initarg-names
  413.                       supplied-initargs)))
  414.           (when generator
  415.         (gather1 `',(car entry))
  416.         (gather1 generator)))))))
  417.  
  418. (defmethod compute-constructor-code ((class std-class)
  419.                      (constructor constructor))
  420.   (let* ((proto (class-prototype class))
  421.      (wrapper (class-wrapper class))
  422.      (defaults (class-default-initargs class))
  423.          (make
  424.            (compute-applicable-methods #'make-instance (list class)))
  425.      (supplied-initarg-names
  426.        (constructor-supplied-initarg-names constructor))
  427.          (default
  428.        (compute-applicable-methods #'default-initargs
  429.                        (list class supplied-initarg-names))) ;?
  430.          (allocate
  431.            (compute-applicable-methods #'allocate-instance (list class)))
  432.          (initialize
  433.            (compute-applicable-methods #'initialize-instance (list proto)))
  434.          (shared
  435.            (compute-applicable-methods #'shared-initialize (list proto t)))
  436.          (code-generators
  437.            (constructor-code-generators constructor))
  438.      (code-generators
  439.        (constructor-code-generators constructor)))
  440.     (flet ((call-code-generator (generator)
  441.          (when (null generator)
  442.            (unless (setq generator (getf code-generators 'fallback))
  443.          (error "No FALLBACK generator?")))
  444.          (funcall generator class wrapper defaults initialize shared)))
  445.       (if (or (cdr make)
  446.           (cdr default)
  447.           (cdr allocate)
  448.           (check-initargs class
  449.                   supplied-initarg-names
  450.                   defaults
  451.                   (append initialize shared)))
  452.       ;; These are basic shared assumptions, if one of the
  453.       ;; has been violated, we have to resort to the fallback
  454.       ;; case.  Any of these assumptions could be moved out
  455.       ;; of here and into the individual code types if there
  456.       ;; was a need to do so.
  457.       (values (call-code-generator nil) 'fallback)
  458.       ;; Otherwise try all the generators until one produces
  459.       ;; code for us.
  460.       (doplist (type generator) code-generators
  461.         (let ((code (call-code-generator generator)))
  462.           (when code (return (values code type)))))))))
  463.  
  464. ;;;
  465. ;;; The facilities are useful for debugging, and to measure the performance
  466. ;;; boost from constructors.
  467. ;;; 
  468.  
  469. (defun map-constructors (fn)
  470.   (let ((nclasses 0)
  471.     (nconstructors 0))
  472.     (labels ((recurse (class)
  473.            (incf nclasses)
  474.            (dolist (constructor (class-constructors class))
  475.          (incf nconstructors)
  476.          (funcall fn constructor))
  477.            (dolist (subclass (class-direct-subclasses class))
  478.          (recurse subclass))))
  479.       (recurse (find-class 't))
  480.       (values nclasses nconstructors))))
  481.  
  482. (defun reset-constructors ()
  483.   (multiple-value-bind (nclass ncons)
  484.       (map-constructors #'install-lazy-constructor-installer )
  485.     (format t "~&~D classes, ~D constructors." nclass ncons)))
  486.  
  487. (defun disable-constructors ()
  488.   (multiple-value-bind (nclass ncons)
  489.       (map-constructors
  490.     #'(lambda (c)
  491.         (let ((gen (getf (constructor-code-generators c) 'fallback)))
  492.           (if (null gen)
  493.           (error "No fallback constructor for ~S." c)
  494.           (set-constructor-code c
  495.                     (funcall gen
  496.                          (constructor-class c)
  497.                          () () () ())
  498.                     'fallback)))))
  499.     (format t "~&~D classes, ~D constructors." nclass ncons)))
  500.  
  501. (defun enable-constructors ()
  502.   (reset-constructors))
  503.  
  504.  
  505. ;;;
  506. ;;; Helper functions and utilities that are shared by all of the code types
  507. ;;; and by the main compute-constructor-code method as well.
  508. ;;; 
  509.  
  510. (defvar *standard-initialize-instance-method*
  511.         (get-method #'initialize-instance
  512.             ()
  513.             (list *the-class-standard-object*)))
  514.  
  515. (defvar *standard-shared-initialize-method*
  516.         (get-method #'shared-initialize
  517.             ()
  518.             (list *the-class-standard-object* *the-class-t*)))
  519.  
  520. (defun non-pcl-initialize-instance-methods-p (methods)
  521.   (notevery #'(lambda (m) (eq m *standard-initialize-instance-method*))
  522.         methods))
  523.  
  524. (defun non-pcl-shared-initialize-methods-p (methods)
  525.   (notevery #'(lambda (m) (eq m *standard-shared-initialize-method*))
  526.         methods))
  527.  
  528. (defun non-pcl-or-after-initialize-instance-methods-p (methods)
  529.   (notevery #'(lambda (m) (or (eq m *standard-initialize-instance-method*)
  530.                   (equal '(:after) (method-qualifiers m))))
  531.         methods))
  532.  
  533. (defun non-pcl-or-after-shared-initialize-methods-p (methods)
  534.   (notevery #'(lambda (m) (or (eq m *standard-shared-initialize-method*)
  535.                   (equal '(:after) (method-qualifiers m))))
  536.         methods))
  537.  
  538.  
  539. ;;; 
  540. ;;; if initargs are valid return nil, otherwise return t.
  541. ;;;
  542. (defun check-initargs (class supplied-initarg-names defaults methods)
  543.   (let ((legal (apply #'append
  544.               (mapcar #'slotd-initargs (class-slots class)))))
  545.     ;; Add to the set of slot-filling initargs the set of
  546.     ;; initargs that are accepted by the methods.  If at
  547.     ;; any point we come across &allow-other-keys, we can
  548.     ;; just quit.
  549.     (dolist (method methods)
  550.       (multiple-value-bind (keys allow-other-keys)
  551.       (function-keywords method)
  552.     (when allow-other-keys
  553.       (return-from check-initargs nil))
  554.     (setq legal (append keys legal))))
  555.     ;; Now check the supplied-initarg-names and the default initargs
  556.     ;; against the total set that we know are legal.
  557.     (dolist (key supplied-initarg-names)
  558.       (unless (memq key legal)
  559.     (return-from check-initargs t)))
  560.     (dolist (default defaults)
  561.       (unless (memq (car default) legal)
  562.     (return-from check-initargs t)))))
  563.  
  564.  
  565. ;;;
  566. ;;; This returns two values.  The first is a vector which can be used as the
  567. ;;; initial value of the slots vector for the instance. The first is a symbol
  568. ;;; describing the initforms this class has.  
  569. ;;;
  570. ;;;  If the first value is:
  571. ;;;
  572. ;;;    :unsupplied    no slot has an initform
  573. ;;;    :constants     all slots have either a constant initform
  574. ;;;                   or no initform at all
  575. ;;;    t              there is at least one non-constant initform
  576. ;;; 
  577. (defun compute-constant-vector (class)
  578.   (declare (values constants flag))
  579.   (let* ((wrapper (class-wrapper class))
  580.      (layout (wrapper-instance-slots-layout wrapper))
  581.      (flag :unsupplied)
  582.      (constants ()))
  583.     (dolist (slotd (class-slots class))
  584.       (let ((name (slotd-name slotd))
  585.         (initform (slotd-initform slotd))
  586.         (initfn (slotd-initfunction slotd)))
  587.     (cond ((null (memq name layout)))
  588.           ((or (eq initform *slotd-unsupplied*)
  589.            (null initfn))
  590.            (push (cons name *slot-unbound*) constants))
  591.           ((constantp initform)
  592.            (push (cons name (eval initform)) constants)
  593.            (when (eq flag ':unsupplied) (setq flag ':constants)))
  594.           (t
  595.            (push (cons name *slot-unbound*) constants)
  596.            (setq flag 't)))))
  597.     (values
  598.       (apply #'vector
  599.          (mapcar #'cdr
  600.              (sort constants #'(lambda (x y)
  601.                      (memq (car y)
  602.                            (memq (car x) layout))))))
  603.       flag)))
  604.  
  605. (defmacro copy-constant-vector (constants)
  606.   `(copy-seq (the simple-vector ,constants)))
  607.  
  608.  
  609. ;;;
  610. ;;; This takes a class and a list of initarg-names, and returns an alist
  611. ;;; indicating the positions of the slots those initargs may fill.  The
  612. ;;; order of the initarg-names argument is important of course, since we
  613. ;;; have to respect the rules about the leftmost initarg that fills a slot
  614. ;;; having precedence.  This function allows initarg names to appear twice
  615. ;;; in the list, it only considers the first appearance.
  616. ;;;
  617. (defun compute-initarg-positions (class initarg-names)
  618.   (let* ((layout (wrapper-instance-slots-layout (class-wrapper class)))
  619.      (positions
  620.        (gathering1 (collecting)
  621.          (iterate ((slot-name (list-elements layout))
  622.                (position (interval :from 0)))
  623.            (gather1 (cons slot-name position)))))
  624.      (slot-initargs
  625.        (mapcar #'(lambda (slotd)
  626.                (list (slotd-initargs slotd)
  627.                  (or (cdr (assq (slotd-name slotd) positions))
  628.                  ':class)))
  629.            (class-slots class))))
  630.     ;; Go through each of the initargs, and figure out what position
  631.     ;; it fills by replacing the entries in slot-initargs it fills.
  632.     (dolist (initarg initarg-names)
  633.       (dolist (slot-entry slot-initargs)
  634.     (let ((slot-initargs (car slot-entry)))
  635.       (when (and (listp slot-initargs)
  636.              (not (null slot-initargs))
  637.              (memq initarg slot-initargs))
  638.         (setf (car slot-entry) initarg)))))
  639.     (gathering1 (collecting)
  640.       (dolist (initarg initarg-names)
  641.     (let ((positions (gathering1 (collecting)
  642.                (dolist (slot-entry slot-initargs)
  643.                  (when (eq (car slot-entry) initarg)
  644.                    (gather1 (cadr slot-entry)))))))
  645.       (when positions
  646.         (gather1 (cons initarg positions))))))))
  647.  
  648.  
  649. ;;;
  650. ;;; The FALLBACK case allows anything.  This always works, and always appears
  651. ;;; as the last of the generators for a constructor.  It does a full call to
  652. ;;; make-instance.
  653. ;;;
  654.  
  655. (define-constructor-code-type fallback
  656.         (class name arglist supplied-initarg-names supplied-initargs)
  657.   (declare (ignore name supplied-initarg-names))
  658.   `(function
  659.      (lambda (&rest ignore)
  660.        (declare (ignore ignore))
  661.        (function
  662.      (lambda ,arglist
  663.        (make-instance
  664.          ',(class-name class)
  665.          ,@(gathering1 (collecting)
  666.          (iterate ((tail (*list-tails supplied-initargs :by #'cddr)))
  667.            (gather1 `',(car tail))
  668.            (gather1 (cadr tail))))))))))
  669.  
  670. ;;;
  671. ;;; The GENERAL case allows:
  672. ;;;   constant, unsupplied or non-constant initforms
  673. ;;;   constant or non-constant default initargs
  674. ;;;   supplied initargs
  675. ;;;   slot-filling initargs
  676. ;;;   :after methods on shared-initialize and initialize-instance
  677. ;;;   
  678. (define-constructor-code-type general
  679.         (class name arglist supplied-initarg-names supplied-initargs)
  680.   (declare (ignore name))
  681.   (let ((raw-allocator (raw-instance-allocator class))
  682.     (slots-fetcher (slots-fetcher class))
  683.     (wrapper-fetcher (wrapper-fetcher class)))
  684.     `(function
  685.        (lambda (class .wrapper. defaults init shared)
  686.      (multiple-value-bind (.constants.
  687.                    .constant-initargs.
  688.                    .initfns-initargs-and-positions.
  689.                    .supplied-initarg-positions.
  690.                    .shared-initfns.
  691.                    .initfns.)
  692.          (general-generator-internal class
  693.                      defaults
  694.                      init
  695.                      shared
  696.                      ',supplied-initarg-names
  697.                      ',supplied-initargs)
  698.        .supplied-initarg-positions.
  699.        (when (and .constants.
  700.               (null (non-pcl-or-after-initialize-instance-methods-p
  701.                   init))
  702.               (null (non-pcl-or-after-shared-initialize-methods-p
  703.                   shared)))
  704.          (function
  705.            (lambda ,arglist
  706.          (declare (optimize (speed 3) (safety 0)))
  707.          (let ((.instance. (,raw-allocator))
  708.                (.slots. (copy-constant-vector .constants.))
  709.                (.positions. .supplied-initarg-positions.)
  710.                (.initargs. .constant-initargs.))           
  711.            .positions.
  712.            
  713.            (setf (,slots-fetcher .instance.) .slots.)         
  714.            (setf (,wrapper-fetcher .instance.) .wrapper.)
  715.  
  716.            (dolist (entry .initfns-initargs-and-positions.)
  717.              (let ((val (funcall (car entry)))
  718.                (initarg (cadr entry)))
  719.                (when initarg
  720.              (push val .initargs.)
  721.              (push initarg .initargs.))
  722.                (dolist (pos (cddr entry))
  723.              (setf (%svref .slots. pos) val))))
  724.  
  725.            ,@(gathering1 (collecting)
  726.                (doplist (initarg value) supplied-initargs
  727.              (unless (constantp value)
  728.                (gather1 `(let ((.value. ,value))
  729.                        (push .value. .initargs.)
  730.                        (push ',initarg .initargs.)
  731.                        (dolist (.p. (pop .positions.))
  732.                      (setf (%svref .slots. .p.)
  733.                            .value.)))))))
  734.  
  735.            (dolist (fn .shared-initfns.)
  736.              (apply fn .instance. t .initargs.))
  737.            (dolist (fn .initfns.)
  738.              (apply fn .instance. .initargs.))
  739.              
  740.            .instance.)))))))))
  741.  
  742. (defun general-generator-internal
  743.        (class defaults init shared supplied-initarg-names supplied-initargs)
  744.   (flet ((bail-out () (return-from general-generator-internal nil)))
  745.     (let* ((constants (compute-constant-vector class))
  746.        (layout (wrapper-instance-slots-layout (class-wrapper class)))
  747.        (initarg-positions
  748.          (compute-initarg-positions class
  749.                     (append supplied-initarg-names
  750.                         (mapcar #'car defaults))))
  751.        (initfns-initargs-and-positions ())
  752.        (supplied-initarg-positions ())
  753.        (constant-initargs ())
  754.        (used-positions ()))
  755.                            
  756.       ;;
  757.       ;; Go through each of the supplied initargs for three reasons.
  758.       ;;
  759.       ;;   - If it fills a class slot, bail out.
  760.       ;;   - If its a constant form, fill the constant vector.
  761.       ;;   - Otherwise remember the positions no two initargs
  762.       ;;     will try to fill the same position, since compute
  763.       ;;     initarg positions already took care of that, but
  764.       ;;     we do need to know what initforms will and won't
  765.       ;;     be needed.
  766.       ;;   
  767.       (doplist (initarg val) supplied-initargs
  768.     (let ((positions (cdr (assq initarg initarg-positions))))
  769.       (cond ((memq :class positions) (bail-out))
  770.         ((constantp val)
  771.          (setq val (eval val))
  772.          (push val constant-initargs)
  773.          (push initarg constant-initargs)
  774.          (dolist (pos positions) (setf (svref constants pos) val)))
  775.         (t
  776.          (push positions supplied-initarg-positions)))
  777.       (setq used-positions (append positions used-positions))))
  778.       ;;
  779.       ;; Go through each of the default initargs, for three reasons.
  780.       ;;
  781.       ;;   - If it fills a class slot, bail out.
  782.       ;;   - If it is a constant, and it does fill a slot, put that
  783.       ;;     into the constant vector.
  784.       ;;   - If it isn't a constant, record its initfn and position.
  785.       ;;   
  786.       (dolist (default defaults)
  787.     (let* ((name (car default))
  788.            (initfn (cadr default))
  789.            (form (caddr default))
  790.            (value ())
  791.            (positions (cdr (assq name initarg-positions))))
  792.       (unless (memq name supplied-initarg-names)
  793.         (cond ((memq :class positions) (bail-out))
  794.           ((constantp form)
  795.            (setq value (eval form))
  796.            (push value constant-initargs)
  797.            (push name constant-initargs)
  798.            (dolist (pos positions)
  799.              (setf (svref constants pos) value)))
  800.           (t
  801.            (push (list* initfn name positions)
  802.              initfns-initargs-and-positions)))
  803.         (setq used-positions (append positions used-positions)))))
  804.       ;;
  805.       ;; Go through each of the slot initforms:
  806.       ;;
  807.       ;;    - If its position has already been filled, do nothing.
  808.       ;;      The initfn won't need to be called, and the slot won't
  809.       ;;      need to be touched.
  810.       ;;    - If it is a class slot, and has an initform, bail out.
  811.       ;;    - If its a constant or unsupplied, ignore it, it is
  812.       ;;      already in the constant vector.
  813.       ;;    - Otherwise, record its initfn and position
  814.       ;;
  815.       (dolist (slotd (class-slots class))
  816.     (let* ((alloc (slotd-allocation slotd))
  817.            (name (slotd-name slotd))
  818.            (form (slotd-initform slotd))
  819.            (initfn (slotd-initfunction slotd))
  820.            (position (position name layout)))
  821.       (cond ((neq alloc :instance)
  822.          (unless (or (eq form *slotd-unsupplied*)
  823.                  (null initfn))
  824.            (bail-out)))
  825.         ((member position used-positions))
  826.         ((or (constantp form)
  827.              (eq form *slotd-unsupplied*)))
  828.         (t
  829.          (push (list initfn nil position)
  830.                initfns-initargs-and-positions)))))
  831.  
  832.       (values constants
  833.           constant-initargs
  834.           (nreverse initfns-initargs-and-positions)
  835.           (nreverse supplied-initarg-positions)
  836.           (mapcar #'method-function
  837.               (remove *standard-shared-initialize-method* shared))
  838.           (mapcar #'method-function
  839.               (remove *standard-initialize-instance-method* init))))))
  840.  
  841.  
  842. ;;;
  843. ;;; The NO-METHODS case allows:
  844. ;;;   constant, unsupplied or non-constant initforms
  845. ;;;   constant or non-constant default initargs
  846. ;;;   supplied initargs that are arguments to constructor, or constants
  847. ;;;   slot-filling initargs
  848. ;;;
  849.  
  850. (define-constructor-code-type no-methods
  851.         (class name arglist supplied-initarg-names supplied-initargs)
  852.   (declare (ignore name))
  853.   (let ((raw-allocator (raw-instance-allocator class))
  854.     (slots-fetcher (slots-fetcher class))
  855.     (wrapper-fetcher (wrapper-fetcher class)))
  856.     `(function
  857.        (lambda (class .wrapper. defaults init shared)
  858.      (multiple-value-bind (.constants.
  859.                    .initfns-and-positions.
  860.                    .supplied-initarg-positions.)
  861.          (no-methods-generator-internal class
  862.                         defaults
  863.                         ',supplied-initarg-names
  864.                         ',supplied-initargs)
  865.        .initfns-and-positions.
  866.        .supplied-initarg-positions.
  867.        (when (and .constants.
  868.               (null (non-pcl-initialize-instance-methods-p init))
  869.               (null (non-pcl-shared-initialize-methods-p shared)))
  870.          #'(lambda ,arglist
  871.          (declare (optimize (speed 3) (safety 0)))
  872.          (let ((.instance. (,raw-allocator))
  873.                (.slots. (copy-constant-vector .constants.))
  874.                (.positions. .supplied-initarg-positions.))
  875.            .positions.
  876.            (setf (,slots-fetcher .instance.) .slots.)
  877.            (setf (,wrapper-fetcher .instance.) .wrapper.)
  878.  
  879.            (dolist (entry .initfns-and-positions.)
  880.              (let ((val (funcall (car entry))))
  881.                (dolist (pos (cdr entry))
  882.              (setf (%svref .slots. pos) val))))
  883.          
  884.            ,@(gathering1 (collecting)
  885.                (doplist (initarg value) supplied-initargs
  886.              (unless (constantp value)
  887.                (gather1
  888.                  `(let ((.value. ,value))
  889.                 (dolist (.p. (pop .positions.))
  890.                   (setf (%svref .slots. .p.) .value.)))))))
  891.              
  892.            .instance.))))))))
  893.  
  894. (defun no-methods-generator-internal
  895.        (class defaults supplied-initarg-names supplied-initargs)
  896.   (flet ((bail-out () (return-from no-methods-generator-internal nil)))
  897.     (let* ((constants    (compute-constant-vector class))
  898.        (layout (wrapper-instance-slots-layout (class-wrapper class)))
  899.        (initarg-positions
  900.          (compute-initarg-positions class
  901.                     (append supplied-initarg-names
  902.                         (mapcar #'car defaults))))
  903.        (initfns-and-positions ())
  904.        (supplied-initarg-positions ())
  905.        (used-positions ()))
  906.       ;;
  907.       ;; Go through each of the supplied initargs for three reasons.
  908.       ;;
  909.       ;;   - If it fills a class slot, bail out.
  910.       ;;   - If its a constant form, fill the constant vector.
  911.       ;;   - Otherwise remember the positions, no two initargs
  912.       ;;     will try to fill the same position, since compute
  913.       ;;     initarg positions already took care of that, but
  914.       ;;     we do need to know what initforms will and won't
  915.       ;;     be needed.
  916.       ;;   
  917.       (doplist (initarg val) supplied-initargs
  918.     (let ((positions (cdr (assq initarg initarg-positions))))
  919.       (cond ((memq :class positions) (bail-out))
  920.         ((constantp val)
  921.          (setq val (eval val))
  922.          (dolist (pos positions)
  923.            (setf (svref constants pos) val)))
  924.         (t
  925.          (push positions supplied-initarg-positions)))
  926.       (setq used-positions (append positions used-positions))))
  927.       ;;
  928.       ;; Go through each of the default initargs, for three reasons.
  929.       ;;
  930.       ;;   - If it fills a class slot, bail out.
  931.       ;;   - If it is a constant, and it does fill a slot, put that
  932.       ;;     into the constant vector.
  933.       ;;   - If it isn't a constant, record its initfn and position.
  934.       ;;   
  935.       (dolist (default defaults)
  936.     (let* ((name (car default))
  937.            (initfn (cadr default))
  938.            (form (caddr default))
  939.            (value ())
  940.            (positions (cdr (assq name initarg-positions))))
  941.       (unless (memq name supplied-initarg-names)
  942.         (cond ((memq :class positions) (bail-out))
  943.           ((constantp form)
  944.            (setq value (eval form))
  945.            (dolist (pos positions)
  946.              (setf (svref constants pos) value)))
  947.           (t
  948.            (push (cons initfn positions)
  949.              initfns-and-positions)))
  950.         (setq used-positions (append positions used-positions)))))
  951.       ;;
  952.       ;; Go through each of the slot initforms:
  953.       ;;
  954.       ;;    - If its position has already been filled, do nothing.
  955.       ;;      The initfn won't need to be called, and the slot won't
  956.       ;;      need to be touched.
  957.       ;;    - If it is a class slot, and has an initform, bail out.
  958.       ;;    - If its a constant or unsupplied, do nothing, we know
  959.       ;;      that it is already in the constant vector.
  960.       ;;    - Otherwise, record its initfn and position
  961.       ;;
  962.       (dolist (slotd (class-slots class))
  963.     (let* ((alloc (slotd-allocation slotd))
  964.            (name (slotd-name slotd))
  965.            (form (slotd-initform slotd))
  966.            (initfn (slotd-initfunction slotd))
  967.            (position (position name layout)))
  968.       (cond ((neq alloc :instance)
  969.          (unless (or (eq form *slotd-unsupplied*) 
  970.                  (null initfn))
  971.            (bail-out)))
  972.         ((member position used-positions))
  973.         ((or (constantp form)
  974.              (eq form *slotd-unsupplied*)))
  975.         (t
  976.          (push (list initfn position) initfns-and-positions)))))
  977.  
  978.       (values constants
  979.           (nreverse initfns-and-positions)
  980.           (nreverse supplied-initarg-positions)))))
  981.  
  982.  
  983. ;;;
  984. ;;; The SIMPLE-SLOTS case allows:
  985. ;;;   constant or unsupplied initforms
  986. ;;;   constant default initargs
  987. ;;;   supplied initargs
  988. ;;;   slot filling initargs
  989. ;;;
  990.  
  991. (define-constructor-code-type simple-slots
  992.         (class name arglist supplied-initarg-names supplied-initargs)
  993.   (declare (ignore name))
  994.   (let ((raw-allocator (raw-instance-allocator class))
  995.     (slots-fetcher (slots-fetcher class))
  996.     (wrapper-fetcher (wrapper-fetcher class)))
  997.     `(function
  998.        (lambda (class .wrapper. defaults init shared)
  999.      (when (and (null (non-pcl-initialize-instance-methods-p init))
  1000.             (null (non-pcl-shared-initialize-methods-p shared)))
  1001.        (multiple-value-bind (.constants. .supplied-initarg-positions.)
  1002.            (simple-slots-generator-internal class
  1003.                         defaults
  1004.                         ',supplied-initarg-names
  1005.                         ',supplied-initargs)
  1006.          (when .constants.
  1007.            (function
  1008.          (lambda ,arglist
  1009.            (declare (optimize (speed 3) (safety 0)))
  1010.            (let ((.instance. (,raw-allocator))
  1011.              (.slots. (copy-constant-vector .constants.))
  1012.              (.positions. .supplied-initarg-positions.))
  1013.              
  1014.              .positions.
  1015.              (setf (,slots-fetcher .instance.) .slots.)         
  1016.              (setf (,wrapper-fetcher .instance.) .wrapper.)
  1017.          
  1018.              ,@(gathering1 (collecting)
  1019.              (doplist (initarg value) supplied-initargs
  1020.                (unless (constantp value)
  1021.                  (gather1
  1022.                    `(let ((.value. ,value))
  1023.                   (dolist (.p. (pop .positions.))
  1024.                     (setf (%svref .slots. .p.) .value.)))))))
  1025.              
  1026.              .instance.))))))))))
  1027.  
  1028. (defun simple-slots-generator-internal
  1029.        (class defaults supplied-initarg-names supplied-initargs)
  1030.   (flet ((bail-out () (return-from simple-slots-generator-internal nil)))
  1031.     (let* ((constants (compute-constant-vector class))
  1032.        (layout (wrapper-instance-slots-layout (class-wrapper class)))
  1033.        (initarg-positions
  1034.          (compute-initarg-positions class
  1035.                     (append supplied-initarg-names
  1036.                         (mapcar #'car defaults))))
  1037.        (supplied-initarg-positions ())
  1038.        (used-positions ()))
  1039.       ;;
  1040.       ;; Go through each of the supplied initargs for three reasons.
  1041.       ;;
  1042.       ;;   - If it fills a class slot, bail out.
  1043.       ;;   - If its a constant form, fill the constant vector.
  1044.       ;;   - Otherwise remember the positions, no two initargs
  1045.       ;;     will try to fill the same position, since compute
  1046.       ;;     initarg positions already took care of that, but
  1047.       ;;     we do need to know what initforms will and won't
  1048.       ;;     be needed.
  1049.       ;;   
  1050.       (doplist (initarg val) supplied-initargs
  1051.     (let ((positions (cdr (assq initarg initarg-positions))))
  1052.       (cond ((memq :class positions) (bail-out))
  1053.         ((constantp val)
  1054.          (setq val (eval val))
  1055.          (dolist (pos positions)
  1056.            (setf (svref constants pos) val)))
  1057.         (t
  1058.          (push positions supplied-initarg-positions)))
  1059.       (setq used-positions (append used-positions positions))))
  1060.       ;;
  1061.       ;; Go through each of the default initargs for three reasons.
  1062.       ;; 
  1063.       ;;   - If it isn't a constant form, bail out.
  1064.       ;;   - If it fills a class slot, bail out.
  1065.       ;;   - If it is a constant, and it does fill a slot, put that
  1066.       ;;     into the constant vector.
  1067.       ;;   
  1068.       (dolist (default defaults)
  1069.     (let* ((name (car default))
  1070.            (form (caddr default))
  1071.            (value ())
  1072.            (positions (cdr (assq name initarg-positions))))
  1073.       (unless (memq name supplied-initarg-names)
  1074.         (cond ((memq :class positions) (bail-out))
  1075.           ((not (constantp form))
  1076.            (bail-out))
  1077.           (t
  1078.            (setq value (eval form))
  1079.            (dolist (pos positions)
  1080.              (setf (svref constants pos) value)))))))
  1081.       ;;
  1082.       ;; Go through each of the slot initforms:
  1083.       ;;
  1084.       ;;    - If its position has already been filled, do nothing.
  1085.       ;;      The initfn won't need to be called, and the slot won't
  1086.       ;;      need to be touched, we are OK.
  1087.       ;;    - If it has a non-constant initform, bail-out.  This
  1088.       ;;      case doesn't handle those.
  1089.       ;;    - If it has a constant or unsupplied initform we don't
  1090.       ;;      really need to do anything, the value is in the
  1091.       ;;      constants vector.
  1092.       ;;
  1093.       (dolist (slotd (class-slots class))
  1094.     (let* ((alloc (slotd-allocation slotd))
  1095.            (name (slotd-name slotd))
  1096.            (form (slotd-initform slotd))
  1097.            (initfn (slotd-initfunction slotd))
  1098.            (position (position name layout)))
  1099.       (cond ((neq alloc :instance)
  1100.          (unless (or (eq form *slotd-unsupplied*)
  1101.                  (null initfn))
  1102.            (bail-out)))
  1103.         ((member position used-positions))
  1104.         ((or (constantp form)
  1105.              (eq form *slotd-unsupplied*)))
  1106.         (t
  1107.          (bail-out)))))
  1108.       
  1109.       (values constants (nreverse supplied-initarg-positions)))))
  1110.